//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Word;

namespace Microsoft.TridentWordAddIn.AddIn
{
    public static class WordExtensions
    {
        /// <summary>
        /// Go to the end of document.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="insertNewLine">if set to <c>true</c> [insert new line].</param>
        public static void MoveToEndOfDocument(this _Application application, bool insertNewLine)
        {
            object endOfDoc = WdUnits.wdStory;
            object moveToEnd = WdMovementType.wdMove;
            if (insertNewLine)
            {
                application.Selection.EndOf(ref endOfDoc, ref moveToEnd);
                application.NewLine();
            }

            application.Selection.EndOf(ref endOfDoc, ref moveToEnd);
            endOfDoc = WdUnits.wdTable;
            application.Selection.EndOf(ref endOfDoc, ref moveToEnd);
        }

        /// <summary>
        /// Moves the selection to end.
        /// </summary>
        /// <param name="application">The application.</param>
        public static void MoveSelectionToEnd(this _Application application)
        {
            object unit = WdUnits.wdParagraph;
            int paragraphCount = application.Selection.Paragraphs.Count;
            if (paragraphCount > 0)
            {
                object noObject = paragraphCount - 1;
                application.Selection.MoveStart(ref unit, ref noObject);
            }

            unit = WdUnits.wdLine;
            object extend = WdMovementType.wdMove;
            application.Selection.EndOf(ref unit, ref extend);
        }

        /// <summary>
        /// Inserts the text.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="text">The text.</param>
        /// <param name="fontSize">Size of the font.</param>
        /// <param name="wdUnderline">The wd underline.</param>
        /// <param name="isBold">if set to <c>true</c> [is bold].</param>
        public static void InsertText(this _Application application, string text, 
            int fontSize, WdUnderline wdUnderline, bool isBold)
        {
            application.MoveSelectionToEnd();
            application.Selection.Text = text;
            application.Selection.Range.Font.Size = fontSize;
            application.Selection.Range.Underline = wdUnderline;
            if (isBold)
            {
                application.Selection.Range.Bold = 1;
            }

            application.Selection.ParagraphFormat.SpaceAfter = 1;
            application.MoveSelectionToEnd();
            application.MoveToEndOfDocument(false);
            application.Selection.Range.Underline = WdUnderline.wdUnderlineNone;
            application.NewLine();
        }

        /// <summary>
        /// News the line.
        /// </summary>
        /// <param name="application">The application.</param>
        public static void NewLine(this _Application application)
        {
            application.Selection.TypeText("\n");
        }

        /// <summary>
        /// Adds the OLE object.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        public static InlineShape AddOleObject(this _Application application, string filePath)
        {
            object package = "Package";
            object index = Type.Missing;
            object iconFilename = Type.Missing;
            object trueObject = true;
            object falseObject = false;
            object filename = filePath;
            object missing = Type.Missing;
            object tag = Path.GetFileName(filePath);

            return application.Selection.Range.InlineShapes.AddOLEObject(
                  ref package,
                  ref filename,
                  ref falseObject,
                  ref trueObject,
                  ref iconFilename, ref index,
                  ref tag,
                  ref missing);
        }

        /// <summary>
        /// Inserts the picture.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="filePath">The file path.</param>
        /// <returns></returns>
        public static InlineShape InsertPicture(this _Application application, string filePath)
        {
            Range range = application.Selection.Range;
            range.InsertBefore(" ");
            object endOfDoc = WdUnits.wdCharacter;
            object moveToEnd = WdMovementType.wdMove;
            object imgRange = range;
            string imgLocation = filePath;
            object trueField = true;
            object falseFeild = false;

            try
            {
                InlineShape insertedPicture = application.ActiveDocument.InlineShapes.AddPicture(imgLocation, ref falseFeild, ref trueField, ref imgRange);
                insertedPicture.LockAspectRatio = MsoTriState.msoFalse;

                if (insertedPicture.Width > 375)
                {
                    float resizeFactor = 375 / insertedPicture.Width;
                    insertedPicture.Width = insertedPicture.Width * resizeFactor;
                }

                if (insertedPicture.Height > 575)
                {
                    float resizeFactor = 575 / insertedPicture.Height;
                    insertedPicture.Height = insertedPicture.Height * resizeFactor;
                }

                moveToEnd = WdMovementType.wdExtend;
                range.EndOf(ref endOfDoc, ref moveToEnd);
                Thread.Sleep(200); // needed for app not to hang
                return insertedPicture;
            }
            catch (ExternalException ex)
            {
                Trace.WriteLine("External exception during insert image:" + ex.StackTrace);
                throw new InvalidOperationException("Failure during clipboard operations.", ex);
            }
        }

        /// <summary>
        /// Protects the document.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="password">The password.</param>
        public static bool ProtectDocument(this _Application application, string password)
        {
            application.MoveToEndOfDocument(true);

            Range range = application.Selection.Range;
            object noReset = false;
            object useIRM = false;
            object enforceStyleLock = false;
            object passwordString = password;

            application.ActiveDocument.Protect(
                Microsoft.Office.Interop.Word.WdProtectionType.wdAllowOnlyReading,
                ref noReset,
                ref passwordString,
                ref useIRM,
                ref enforceStyleLock);

            object editorId = Microsoft.Office.Interop.Word.WdEditorType.wdEditorEveryone;
            range.Editors.Add(ref editorId);
            return true;
        }

        /// <summary>
        /// Uns the protect document.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        public static bool UnprotectDocument(this _Application application, object password)
        {
            object editorId = Microsoft.Office.Interop.Word.WdEditorType.wdEditorEveryone;
            Editor editor = application.Selection.Range.Editors.Item(ref editorId);
            application.ActiveDocument.Unprotect(ref password);
            if (editor != null)
            {
                editor.Delete();
            }

            return true;
        }

        /// <summary>
        /// Creates the table.
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="tableRange">The table range.</param>
        /// <returns></returns>
        public static Table CreateTable(this _Application application, string[] columnName, Range tableRange)
        {
            application.MoveSelectionToEnd();
            object autoFitFixed = WdAutoFitBehavior.wdAutoFitContent;
            object defaultBehaviorWord9 = WdDefaultTableBehavior.wdWord9TableBehavior;
            Range range = application.Selection.Range;
            Table table = range.Tables.Add(tableRange, 1, columnName.Length, ref defaultBehaviorWord9, ref autoFitFixed);
            table.Borders.Enable = 1;
            table.Range.Font.Size = 9;
            table.Range.Font.Name = "Calibri";
            table.Borders.InsideLineStyle = WdLineStyle.wdLineStyleNone;

            int index = 1;
            foreach (string name in columnName)
            {
                Range cellRange = table.Cell(1, index).Range;
                cellRange.Bold = 2;
                cellRange.Text = name;
                index++;
            }

            return table;
        }

        /// <summary>
        /// Adds the row.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="values">The values.</param>
        public static void AddRow(this Table table, string[] values)
        {
            object objMissing = System.Reflection.Missing.Value;
            table.Rows.Add(ref objMissing);

            int columnCount = 1;
            int rowCount = table.Rows.Count;
            foreach (string value in values)
            {
                table.Cell(rowCount, columnCount).Range.Bold = 0;
                table.Cell(rowCount, columnCount).Range.Text = value;
                columnCount++;
            }
        }

        /// <summary>
        /// Formats the table header.
        /// </summary>
        /// <param name="table">The table.</param>
        public static void FormatTableHeader(this Table table)
        {
            Row row = table.Rows[1];
            row.Range.Shading.BackgroundPatternColor = WdColor.wdColorGray20;
            table.Range.ParagraphFormat.SpaceAfter = 0.0F;
            table.Range.ParagraphFormat.SpaceBefore = 0.0F;
        }
    }
}
